#ifndef INDEX_H_INCLUDED
#define INDEX_H_INCLUDED

#include "Node.h"
#include "DB.h"
#include "Leaf.h"

/**
A template-class for indexes with arbitrary key-types with fixed length
*/
template<class T>
class Index
{
public:
    Index() : indexSegment(DB::getDB().getSegmentManager().createSegment()), context(NULL), comparator(NULL) {
        indexSegment.requestPages(1,1);
        BufferFrame& page = indexSegment.getPage(0, true);
        NodeContext* context = createContext();
        Leaf::fromNew(page, *context);
        this->indexSegment.unfixPage(page, true);
        this->rootNode = new Node(0, *context);
    }

    Index(unsigned keyLength) : indexSegment(DB::getDB().getSegmentManager().createSegment()) {
        indexSegment.requestPages(1,1);
        BufferFrame& page = indexSegment.getPage(0, true);
        Leaf::fromNew(page, *createContext());
        this->indexSegment.unfixPage(page, true);
        this->rootNode = new Node(0, createContext());
    }

    bool insert(T key, TID tupelId);
    bool remove(T key);
    TID lookup(T key);
    index_iterator lookupRange(T lowerKey, T upperKey);
    ~Index()
    {
        delete rootNode;
        delete context;
        delete comparator;
    }

    void visualize(IndexVisualizer& vis);

private:
    Segment& indexSegment;
    Node* rootNode;
    NodeContext* context;
    Comparator* comparator;

    Comparator createComparator();
    NodeContext* createContext();
    NodeContext* createContext(unsigned keySize);

    static bool smaller(char* one, char* other);
    static bool smallerOrEqual(char* one, char* other);
    static bool greater(char* one, char* other);
    static bool greaterOrEqual(char* one, char* other);
    static bool equal(char* one, char* other);
    static string toString(char* x);
};

//freaky... ^^
template<class T> Comparator Index<T>::createComparator()
{
    comparator = new Comparator(&smaller, &smallerOrEqual, &greater, &greaterOrEqual, &equal);
    return *comparator;
}

template<class T> NodeContext* Index<T>::createContext()
{
    return createContext(sizeof(T));
}

template<class T> NodeContext* Index<T>::createContext(unsigned keySize)
{
    context = new NodeContext(keySize, createComparator(), indexSegment);
    return context;
}

template<class T> bool Index<T>::insert(T key, TID tupel)
{
    return rootNode->insert((char*)&key, tupel);
}

template<class T> bool Index<T>::remove(T key)
{
    RemovalResult* remResult = rootNode->remove((char*)&key, true);
    bool result = remResult->removed;
    delete remResult;
    return result;
}

template<class T> TID Index<T>::lookup(T key)
{
    return rootNode->lookup((char*)&key);
}

template<class T> index_iterator Index<T>::lookupRange(T lowerKey, T upperKey)
{
    return rootNode->lookupRange((char*)&lowerKey, (char*)&upperKey);
}

template<class T> void Index<T>::visualize(IndexVisualizer& vis)
{
    vis.addToStringMethod(toString);
    rootNode->visualize(vis);
}

/* template function to wrapp the compare-functions */
template<class T> bool Index<T>::smaller (char* one, char* other)
{
    return *((T*)one) < *((T*)other);
}

template<class T> bool Index<T>::smallerOrEqual (char* one, char* other)
{
    return *((T*)one) <= *((T*)other);
}

template<class T> bool Index<T>::greater (char* one, char* other)
{
    return *((T*)one) > *((T*)other);
}

template<class T> bool Index<T>::greaterOrEqual (char* one, char* other)
{
    return *((T*)one) >= *((T*)other);
}

template<class T> bool Index<T>::equal (char* one, char* other)
{
    return *((T*)one) == *((T*)other);
}

template<class T> string Index<T>::toString(char* x)
{
    ostringstream s;
    s << *((T*)x);
    return s.str();
}

#endif // INDEX_H_INCLUDED
